package com.mcarthur.countdown.client.drawable;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.Stack;

import com.googlecode.gwtgl.binding.WebGLRenderingContext;
import com.mcarthur.countdown.client.renderer.CountdownRenderer;

/**
 * Manages balls :-)
 */
public class BouncyBallManager {
	private static final int NUM_BALLS = 50;
	private static final float MAX_VEL = 0.5f; //10
//	private static final float GRAVITY = 0.001f;  //.8f
//	private static final float KINETIC_DAMPER = -0.9f;
//	private static final float KINETIC_DAMPER_X = 0.96f;
	
	private static BouncyBallManager instance;
	
	private Queue<BouncyBall> activeBalls;
	private Stack<BouncyBall> inactiveBalls;
//	private List<BouncyBall> collidedBalls;
	private BallAnimator animator;
	
	// Singleton
	private BouncyBallManager() {
	    
		activeBalls = new LinkedList<BouncyBall>();
		inactiveBalls = new Stack<BouncyBall>();
		for (int i = 0; i < NUM_BALLS; i++) {
			inactiveBalls.add(new BouncyBall());
		}
		animator = new DefaultAnimator(activeBalls, inactiveBalls);
//		collidedBalls = new ArrayList<BouncyBall>();
	}
	
	public static BouncyBallManager get() {
		if (instance == null) {
			instance = new BouncyBallManager();
		}
		return instance;
	}
	
	/**
	 * Activates a fresh bouncy ball.
	 */
	public void activateBouncyBall(Ball parentBall) {
		BouncyBall activeBall = null;
		
		if (!inactiveBalls.isEmpty()) {
			activeBall = inactiveBalls.pop();
			activeBalls.add(activeBall);
		} else {
			activeBall = activeBalls.remove();
			activeBalls.add(activeBall);
		}
		
		activeBall.setActive(true);
		activeBall.setPosX(parentBall.getPosX());
		activeBall.setPosY(parentBall.getPosY());
		activeBall.setActiveColor(parentBall.getActiveColor());
		
		randomize(activeBall);
	}
	
	private long drawTime;
	
	public void draw(WebGLRenderingContext gl) {
		long time = System.currentTimeMillis();
		if (drawTime == 0) {
			drawTime  = time;
		}
		
		long dt =  time - drawTime;
		drawTime = time;
		
		animator.draw(gl, dt);
	}
	
	public void onResume() {
		drawTime = 0;
	}
	
//	private void checkBounds(BouncyBall ball) {
//		// Concurrent mod exception ;-( might not be needed tho
//		if (ball.getPosX() <= -Ball.WIDTH || ball.getPosX() >= CountdownRenderer.viewWidth) {
////			activeBalls.remove(ball);
////			inactiveBalls.push(ball);
////			return;
//		}
//		if (ball.getPosY() < 0) {
//			//ball.setPosY(ball.getPosY() - ball.getVelY());
//			ball.setPosY(0.f);
//			ball.setVelY(ball.getVelY() * KINETIC_DAMPER);
//			ball.setVelX(ball.getVelX() * KINETIC_DAMPER_X);
//			
//
////			Vector2 old = new Vector2(ball.getVelX(), ball.getVelY()).getUnit();
////			Vector2 normal = new Vector2(0.f, -1.f);
////			Vector2 vNew = normal.mult(2 * (old.mult(-1.f).dot(normal))).add(old);
////			ball.setVelX(vNew.x);
////			ball.setVelY(vNew.y);
//		}
//	}
	
//	private void checkCollision(BouncyBall ball) {
//		
//		// BEGIN INSANE PHYSICS
//		
//		for (BouncyBall other : activeBalls) {
//			if (ball == other || collidedBalls.contains(other)) {
//				continue;
//			}
//			
//			Vector2 axis = ball.getCenter().sub(other.getCenter());
//			if (axis.getMagnitude() < (Ball.WIDTH)) {
//				// collision!
//				axis = axis.getUnit();
//				
//				Vector2 b1 = new Vector2(ball.getVelX(), ball.getVelY());
//				Vector2 b1x = axis.mult((axis.dot(b1)));
//				Vector2 b1y = b1.sub(b1x);
//				
//				Vector2 b2 = new Vector2(other.getVelX(), other.getVelY());
//				Vector2 negAxis = axis.mult(-1);
//				Vector2 b2x = negAxis.mult(negAxis.dot(b2));
//				Vector2 b2y = b2.sub(b2x);
//				
//				Vector2 v1x = (b2x.mult(2)).mult(0.5f);
//				Vector2 v2x = (b1x.mult(2)).mult(0.5f);
//				
//				Vector2 v1y = b1y;
//				Vector2 v2y = b2y;
//
//				Vector2 v1 = v1x.add(v1y);
//				Vector2 v2 = v2x.add(v2y);
//				
//				ball.setVelX(v1.x);
//				ball.setVelY(v1.y);
//				
//				other.setVelX(v2.x);
//				other.setVelY(v2.y);
//			}
//		}
//		
//		collidedBalls.add(ball);
//
//		// END
//	}

	private void randomize(BouncyBall ball) {
		Random random = new Random();
		float velX = random.nextFloat() * MAX_VEL;
		if (random.nextBoolean()) {
			velX = -velX;
		}
		float velY = random.nextFloat() * MAX_VEL;
		if (random.nextBoolean()) {
			velY = -velY;
		}
		
		ball.setVelX(velX);
		ball.setVelY(velY);
	}
	
	public void setAnimator(BallAnimator animator) {
	    this.animator.stop();
	    this.animator = animator;
	    animator.setActiveBalls(activeBalls);
	    animator.setInactiveBalls(inactiveBalls);
	}
}
